package com.lpwa.framework.netty;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lpwa.common.DataUtils;
import com.lpwa.common.utils.DateUtils;
import com.lpwa.common.utils.StaticRedisUtil;
import com.lpwa.common.utils.StringConvert;
import com.lpwa.common.utils.StringUtils;
import com.lpwa.framework.netty.entity.AoaResult;
import com.lpwa.framework.netty.entity.AoaResultRaw;
import com.lpwa.framework.netty.entity.IdentifyReq;
import com.lpwa.framework.netty.entity.LocatorMacCtx;
import com.lpwa.framework.netty.utils.ListUtilsMain;
import com.lpwa.framework.netty.utils.NettyByteAndStringUtils;
import com.lpwa.framework.netty.utils.StaticTagLocatorMapper;
import com.lpwa.framework.websocket.controller.WebSocketServer;

import com.lpwa.system.domain.Tag;
import com.lpwa.system.mapper.LableMapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author June
 */
@Slf4j
public class NettyDataSvervice {
//    @Autowired
//    private LableMapper lableMapper;
    private static final ReentrantLock Lock = new ReentrantLock();
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> sendChannel_map = new ConcurrentHashMap<>();
    /**
     * 设置全局变量
     */
    private static final ConcurrentHashMap<ChannelId, List<String>> SALVE_MAP = new ConcurrentHashMap<>();
    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    private static final ConcurrentHashMap<String, ChannelHandlerContext> MAC_MAP = new ConcurrentHashMap<>();
    private static final CopyOnWriteArrayList<String> CONNHANDLE_List = new CopyOnWriteArrayList();
    private static final ConcurrentHashMap<String, List<AoaResultRaw>> AOA_RES_MAP = new ConcurrentHashMap<>();
    /**
     * 将传送过来的数据进行解析，包括异或运算 （第一次服务器端给客户端发）
     *
     * @param ReceiveData
     *
     * @return
     */
    public static String sendData(String ReceiveData, ChannelHandlerContext ctx) {
        final ReentrantLock putLock = Lock;
        putLock.lock();
        try {
            //此处需要读数据进行校验以及分包黏包处理，本文主要提供思路所以省略
            /*
            *   处理分包黏包、拆分、解析等
            */
            //数据解析
            try {
                 parseData(ReceiveData,ctx);
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                //数据帧WebSocket推送
                WebSocketServer.BroadCastInfo(ReceiveData);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return ReceiveData;
        } finally {
            putLock.unlock();
        }
    }

    /**
     * 数据入库处理
     */
    public static void parseData(String ReceiveData,ChannelHandlerContext ctx) throws Exception{
        //1.判断传入数据类型
        String cmd1 = ReceiveData.substring(8,10);
        String cmd0 = ReceiveData.substring(6,8);
        String verification = ReceiveData.substring(0,2);
        if (cmd1.equals("20")&&verification.equals("fe")&&cmd0.equals("59")){
            //第一步：初始化设备
            //初始化设备后执行扫描操作
            byte[] data = NettyByteAndStringUtils.hexToByte("FE0000390039");
            ctx.writeAndFlush(data);
        }else if (cmd1.equals("00")&&verification.equals("fe")&&cmd0.equals("79")){ //处理RTLS_CMD_IDENTIFY初始化设备返回数据
            log.info("RTLS_CMD_IDENTIFY初始化设备返回数据" + ReceiveData);
            //判断是否是基站（）
            String isCheckLocator = ReceiveData.substring(10,12);
            if (isCheckLocator.equals("84")){ //"84"为基站
                String dataLen = ReceiveData.substring(2,6);
                //小端转大端
                byte[] len = StringConvert.string2Bytes(dataLen);
                long length = StringConvert.getLong(len,true);
                //数据域数据处理
                String payload = ReceiveData.substring(10, (int) (10+(length*2)));
//            log.info("原始数据为payload："+payload);
                //解析数据域字段
                IdentifyReq identifyReq = new IdentifyReq();
                identifyReq.setCapab(payload.substring(0,4));
                identifyReq.setRevNum(payload.substring(4,8));
                identifyReq.setDevId(payload.substring(8,10));
                identifyReq.setIdentifier(payload.substring(10,22));
                identifyReq.setMaxNumConns(payload.substring(22,24));
                //保存通道和mac地址的关系
                MAC_MAP.put(identifyReq.getIdentifier(),ctx);
                String locatorMac = payload.substring(10,22);
                LocatorMacCtx locatorMacCtx = new LocatorMacCtx();
                locatorMacCtx.setChannelId(ctx.channel().id().toString());
                locatorMacCtx.setMac(locatorMac);
                String locatorMacs = StaticRedisUtil.staticRedisUtil.get("locatorMacs")==null?"":StaticRedisUtil.staticRedisUtil.get("locatorMacs").toString();
                if (!StringUtils.isEmpty(locatorMacs)){
                    List<LocatorMacCtx> list = new ArrayList<>();
                    list = JSONObject.parseArray(locatorMacs, LocatorMacCtx.class);
                    for (LocatorMacCtx l:list){
                        if (!l.getMac().equals(locatorMac)){//已存在此mac
                            list.add(locatorMacCtx);
                            StaticRedisUtil.set("locatorMacs",JSON.toJSONString(list));
                        }
                    }

                }else {
                    List<LocatorMacCtx> list = new ArrayList<>();
                    list.add(locatorMacCtx);
                    StaticRedisUtil.set("locatorMacs",JSON.toJSONString(list));
                }
                //获取基站地址解析Identifieri
//            log.info("数据解析"+ JSON.toJSONString(identifyReq));
                //初始化设备后执行扫描操作   --
            byte[] scanData = NettyByteAndStringUtils.hexToByte("FE000039043D");
            ctx.writeAndFlush(scanData);
            }

        }else if (cmd1.equals("04")&&verification.equals("fe")){ //处理RTLS_CMD_SCAN扫描设备返回数据
            if (cmd0.equals("59")){
                log.info("RTLS_CMD_SCAN INFO  59扫描设备返回数据" + ReceiveData);
                String address= ReceiveData.substring(14,26);//slave地址
                String rssi = ReceiveData.substring(26,28);//信号强度
                List<String> addrlist = SALVE_MAP.get(ctx.channel().id());
                if (StringUtils.isEmpty(addrlist)){
                    List<String> list = new ArrayList<>();
                    list.add(address);
                    SALVE_MAP.put(ctx.channel().id(),list);
                }else{
                    addrlist.add(address);
                }
            }else if (cmd0.equals("79")){
                log.info("RTLS_CMD_SCAN REQ 79扫描设备返回数据" + ReceiveData);
            }
        }else if (cmd1.equals("05")&&verification.equals("fe")&&cmd0.equals("59")) {//RTLS_CMD_SCAN_STOP  扫描结束
            //扫描结束后开始连接  发送SALVE连接指令
            log.info("RTLS_CMD_SCAN扫描结束返回数据" + ReceiveData);
            List<String> addrlist = SALVE_MAP.get(ctx.channel().id());
            //查询mac集合
//            List<String> lableList = lableMapper.selectTagIds();
            if(!StringUtils.isEmpty(addrlist)){
                for (int i = 0 ;i<addrlist.size();i++){
//                    if (i>=3){
//                        break;
//                    }
                    String addr = addrlist.get(i);
                    String connStr = "FE0900390300"+addr+"9001";
                    //异或校验
                    String xor = getCheckXOR(connStr.substring(2,connStr.length()));
                    if (xor.length() ==1){
                        xor = "0"+xor;
                    }
                    if (addr.equals("1d51cded0418")){ //8d077503ee04,6121eb03ee04,652a2c5e8af8,6121eb03ee04,F88A5E2C2A75,43522c5e8af8,1d51cded0418
                        byte[] scanData = NettyByteAndStringUtils.hexToByte(connStr+xor);
                        log.info("开始发送SALVE MAC地址为："+addr+"发送SALVE连接指令："+connStr+xor);
                        try {
                            ctx.writeAndFlush(scanData);
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

//                    ctx.writeAndFlush(scanData);

                }
//                for (String addr:addrlist){
//                    String connStr = "FE0900390300"+addr+"9001";
//                    //异或校验
//                    String xor = getCheckXOR(connStr.substring(2,connStr.length()));
//                    if (xor.length() ==1){
//                        xor = "0"+xor;
//                    }
//                    byte[] scanData = NettyByteAndStringUtils.hexToByte(connStr+xor);
//                    log.info("开始发送SALVE MAC地址为："+addr+"发送SALVE连接指令："+connStr+xor);
////                    ctx.writeAndFlush(scanData);
//                    try {
//                        ctx.writeAndFlush(scanData);
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
////                    break;
//                }
                //全部连接后删除掉SALVE_MAP中的值
                SALVE_MAP.remove(ctx.channel().id());
            }
        }else if (cmd1.equals("03")&&verification.equals("fe")){//RTLS_CMD_CONNECT REQ  连接SALVE返回数据
            if (cmd0.equals("79")){
                log.info("RTLS_CMD_CONNECT REQ连接SALVE返回数据" + ReceiveData);
            }else if (cmd0.equals("59")){
                //RTLS_CMD_CONNECT RES   //5-10秒  获取connHandle
                log.info("RTLS_CMD_CONNECT RES连接SALVE返回数据" + ReceiveData);
                String connHandle = ReceiveData.substring(10,14);
                CONNHANDLE_List.add(connHandle);
                String xorData = "2C0039130102"+connHandle+"02040211012200000101020203030000040405050606000007070808090900000A0A0B0B0C0C0000";
                String xor = getCheckXOR(xorData);
                String setData = "FE"+"2C0039130102"+connHandle+"02040211012200000101020203030000040405050606000007070808090900000A0A0B0B0C0C0000"+xor;
                byte[] aoaSetParamsData = NettyByteAndStringUtils.hexToByte(setData);
                log.info("发送RTLS_CMD_AOA_SET_PARAMS设置参数指令："+setData);
//                ctx.writeAndFlush(aoaSetParamsData);
                ctx.writeAndFlush(aoaSetParamsData);
            }
        }else if (cmd1.equals("02")&&verification.equals("fe")&&cmd0.equals("59")){
            log.info("RTLS_CMD_CONN_PARAMS连接SALVE返回PARAMS数据" + ReceiveData);
//            String 保存//connHandle和MAC的关系
        }else if (cmd1.equals("13")&&verification.equals("fe")&&cmd0.equals("79")){
            log.info("RTLS_CMD_AOA_SET_PARAMS RES返回数据" + ReceiveData);
            //RTLS_CMD_AOA_ENABLE 指令下发
//            String connHandle = ReceiveData.substring(10,14);
            if(CONNHANDLE_List!=null&&!CONNHANDLE_List.isEmpty()){
                for (String connHandle: CONNHANDLE_List){
                    //异或校验
                    String xor = getCheckXOR("06003914"+connHandle+"01010014");
                    String enableData = "FE"+"06003914"+connHandle+"01010014"+xor;
                    byte[] aoaEnableData = NettyByteAndStringUtils.hexToByte(enableData+xor);
                    log.info("发送RTLS_CMD_AOA_ENABLE指令："+enableData+xor);
//                    ctx.writeAndFlush(aoaEnableData);
                    ctx.writeAndFlush(aoaEnableData);
                    CONNHANDLE_List.remove(connHandle);
                    Thread.sleep(1000);
                }
            }
        }else if (cmd1.equals("14")&&verification.equals("fe")&&cmd0.equals("79")){
            log.info("RTLS_CMD_AOA_ENABLE REQ返回数据" + ReceiveData);
        }else if (cmd1.equals("24")&&verification.equals("fe")){
//            log.info("RTLS_CMD_AOA_RESULT_RAW设备持续发送数据" + ReceiveData);
            //异或校验检测最后一个字节对不对
            StaticRedisUtil.staticRedisUtil.incr("Count",1);
            String checkData = ReceiveData.substring(2,ReceiveData.length()-2);
            String xor = getCheckXOR(checkData);
            if (xor.length() ==1){
                xor = "0"+xor;
            }
            String orXor = ReceiveData.substring(ReceiveData.length()-2,ReceiveData.length());
            if (!orXor.equals(xor)){
                log.error("异或校验位异常：" + ReceiveData);
                StaticRedisUtil.staticRedisUtil.incr("xorExceptCount",1);
            }else {
                String leng = ReceiveData.substring(2,6);
                //小端转大端
                byte[] lengbyte = StringConvert.string2Bytes(leng);
                long lenglong = StringConvert.getLong(lengbyte,true);
                String connHandle = ReceiveData.substring(10,14);
                String mac = ReceiveData.substring(14,26);
                String rssi = ReceiveData.substring(26,28);   //16
                String channel = ReceiveData.substring(30,32);
                String offset = ReceiveData.substring(32,36);
                String samplesLength = ReceiveData.substring(36,40);
                long l = lenglong*2;
                String playload = ReceiveData.substring(40,(int)lenglong*2+10);
                byte[] len = StringConvert.string2Bytes(samplesLength);
                long length = StringConvert.getLong(len,true);
                byte[] offsetlen = StringConvert.string2Bytes(offset);
                long offsetlength = StringConvert.getLong(offsetlen,true);
                demode(connHandle,channel,(int)offsetlength,(int)length,playload,rssi,mac,ctx.channel().id().toString());
            }
        }else if (cmd1.equals("00")&&verification.equals("fe")&&cmd0.equals("01")){
            //保存通道  fe0000010001
            //如果map中不包含此连接，就保存连接
            if (sendChannel_map.containsKey(ctx.channel().id())) {
                log.info("保存中间件通道 " + sendChannel_map.size());
            } else {
                //保存连接
                sendChannel_map.put(ctx.channel().id(),ctx);
            }

        }else if (cmd1.equals("06")&&verification.equals("fe")&&cmd0.equals("59")){
            log.info("获取基站配置请求：" + ReceiveData);
            //获取基站mac地址   FE 0C 00 59 06    84 00 02 00 05 05 2C 2C 5E 8A F8 08   F1
            String macLe = ReceiveData.substring(20,32); //小端基站
            //通过基站查询配置的标签mac地址   mac转换为大端
            String macBig = "";
            //小端转大端
            macBig = DataUtils.converLeAndBig(macLe);
            //mysql查询基站匹配的标签
            List<String> tagMacList = StaticTagLocatorMapper.staticTagLocatorMapper.selectTagIdsByLocatorId(macBig);
            List<String> tagMacs = new ArrayList<>();
            if (!tagMacList.isEmpty()){
                for (String mac: tagMacList){
                    tagMacs.add(DataUtils.converLeAndBig(mac));
                }
                //下发标签给基站时，每一次下发最多只能有20个tagmac组合而成
                int size = tagMacs.size();
                if (size<=20){
                    String cmd = "";
                    //数据域字节长度
                    int length = 2+2+2+(6*size);
                    //length转16进制小端
                    String leLength = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(length));
                    String cmd00 = "39";
                    String cmd01 = "06";
                    // 本帧设备数量  总设备数 都为size ，size转16小端
                    String deviceNum = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(size));
                    String totalNum = deviceNum;
                    //偏移量为0000
                    //设备列表
                    String tagmacs = "";
                    for (String s: tagMacs){
                        tagmacs= tagmacs+s;
                    }
                    //异或校验
                    String xor = getCheckXOR(leLength+cmd00+cmd01+deviceNum+"0000"+totalNum+tagmacs);
                    if (xor.length() ==1){
                        xor = "0"+xor;
                    }
                    //所以最后
                    cmd = "FE"+leLength+cmd00+cmd01+deviceNum+"0000"+totalNum+tagmacs+xor;
                    //发送给基站
                    byte[] scanData = NettyByteAndStringUtils.hexToByte(cmd);
                    ctx.writeAndFlush(scanData);
                }else {
                    int count = 0;//下发次数
                    //取余
                    int remainder = size%20;
                    if (remainder==0){
                        count = size/20;
                    }else {
                        count = size/20+1;
                    }
                    int yetCount= 0;
                    String addrs = "";
                    for (int i=0;i<size;i++){
                        if (yetCount+1==count){//判断这一次是否是最后一次下发
                            addrs = addrs+tagMacs.get(i);
                            if (i==size-1){
                                //下发最后一组数据
                                String cmd = "";
                                //数据域字节长度
                                int length = (6*remainder)+6;
                                //length转16进制小端
                                String leLength = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(length));
                                String cmd00 = "39";
                                String cmd01 = "06";
                                // 本帧设备数量  总设备数 都为size ，size转16小端
                                String deviceNum = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(remainder));
                                String totalNum = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(size));
                                //偏移量为 i-20
                                String leOffset = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(yetCount*20));
                                //设备列表
                                String tagmacs = addrs;
                                //异或校验
                                String xor = getCheckXOR(leLength+cmd00+cmd01+deviceNum+leOffset+totalNum+tagmacs);
                                if (xor.length() ==1){
                                    xor = "0"+xor;
                                }
                                //所以最后
                                cmd = "FE"+leLength+cmd00+cmd01+deviceNum+leOffset+totalNum+tagmacs+xor;
                                //发送给基站
                                byte[] scanData = NettyByteAndStringUtils.hexToByte(cmd);
                                ctx.writeAndFlush(scanData);
                            }
                        }else {
                            //判断tagmac组装了多长
                            addrs = addrs+tagMacs.get(i);
                            if (addrs.length()==12*20){
                                //下发 20一组下发
                                String cmd = "";
                                //数据域字节长度
                                int length = 2+2+2+(6*20);
                                //length转16进制小端
                                String leLength = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(length));
                                String cmd00 = "39";
                                String cmd01 = "06";
                                // 本帧设备数量  总设备数 都为size ，size转16小端
                                String deviceNum = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(20));
                                String totalNum = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(size));
                                //偏移量为 i
                                String leOffset = DataUtils.converLeAndBig(DataUtils.intTohexTowByte(yetCount*20));
                                //设备列表
                                String tagmacs = addrs;
                                //异或校验
                                String xor = getCheckXOR(leLength+cmd00+cmd01+deviceNum+leOffset+totalNum+tagmacs);
                                if (xor.length() ==1){
                                    xor = "0"+xor;
                                }
                                //所以最后
                                cmd = "FE"+leLength+cmd00+cmd01+deviceNum+leOffset+totalNum+tagmacs+xor;
                                //发送给基站
                                byte[] scanData = NettyByteAndStringUtils.hexToByte(cmd);
                                ctx.writeAndFlush(scanData);
                                Thread.sleep(50);
                                yetCount++;
                                //重置addrs
                                addrs = "";
                            }
                        }

                    }
                }
            }

        }
        else{
            //垃圾数据统计
            StaticRedisUtil.staticRedisUtil.incr(ctx.channel().id().toString()+"junkData",1);
            log.info("垃圾数据" + ReceiveData);
        }
    }
    public static void demode(String connHandle, String channel, int offsetlength, int length, String playload,String rssi,String mac,String channelID){
        AoaResultRaw aoaResultRaw = new AoaResultRaw();
        aoaResultRaw.setConnHandle(connHandle);
        aoaResultRaw.setChannel(channel);
        aoaResultRaw.setOffset(offsetlength);
        aoaResultRaw.setSamplesLength(length);
        aoaResultRaw.setPlayload(playload);
        aoaResultRaw.setMac(mac);
        List<AoaResultRaw> aoalist = AOA_RES_MAP.get(connHandle);
        if (StringUtils.isEmpty(aoalist)){
            List<AoaResultRaw> list = new ArrayList<>();
            list.add(aoaResultRaw);
            AOA_RES_MAP.put(connHandle,list);
        }else{
            aoalist.add(aoaResultRaw);
            int dataleng = playload.length()/2;
            if (length-offsetlength<=32){//总长度减去偏移量小于32则说明这是最后一组数据
                //按照偏移量大小对list进行排序
//            ListUtilsMain.sort(aoalist, true, "offset");
                String offdata = "";
                String smlendata = "";
                StringBuffer playloadBuf = new StringBuffer();
                for (AoaResultRaw raw:aoalist){
                    playloadBuf.append(raw.getPlayload());
                }
                AoaResult aoaResult = new AoaResult();
                aoaResult.setConnHandle(connHandle);
                aoaResult.setChannel(channel);
                aoaResult.setRssi(rssi);
                aoaResult.setMac(mac);
                aoaResult.setPlayloadLength(playloadBuf.length()/8);
                aoaResult.setCreateTime(DateUtils.dateTimeNow());
                aoaResult.setPlayload(playloadBuf.toString());
                String json = JSON.toJSONString(aoaResult);
                StaticRedisUtil.staticRedisUtil.set("aoaResultRawList",json);
//                log.info(" json:"+json);
                //通道数据统计
                StaticRedisUtil.staticRedisUtil.incr(channelID+"_"+mac+"_DataCount",1);
                AOA_RES_MAP.remove(connHandle);
            }
        }

    }
    /**
     *校验和 互相异或 计算
     * 传字符串
     * 例如：“AA55040002DA”
     * 结果：23
     */
    public static String getCheckXOR(String s){
        String regex = "(.{2})";
        String replace = s.replaceAll(regex, "$1 ");
        String[] st = replace.split(" ");
        int[] c = new int[st.length+1];
        for (int i = 0; i <st.length; i++) {
            int i1 = Integer.parseInt(st[i], 16);
            c[i]=i1;
        }
        char x = 0;
        for (int i = 0; i < c.length; i++)
            x ^= c[i];
        return String.format("%x", (int)x);
    }

    /**
     * 字节数组转16进制
     * @param bytes 需要转换的byte数组
     * @return  转换后的Hex字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if(hex.length() < 2){
                sb.append(0);
            }
            sb.append(hex);
        }
        return sb.toString();
    }
}
